﻿using System;
using System.Diagnostics;
using System.ServiceModel;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

namespace TfsGiggle.Core
{
    /// <summary>
    /// The kind of errors that can be returned to the client
    /// </summary>
    public enum ErrorType
    {
        /// <summary>
        /// No error
        /// </summary>
        None,

        /// <summary>
        /// Lucene did not like your query
        /// </summary>
        LuceneParseException,

        /// <summary>
        /// We don't know what happened
        /// </summary>
        UnknownException
    }

    /// <summary>
    /// Enable clients to connect to the search service
    /// </summary>
    public class Client : IDisposable
    {
        private ChannelFactory<IAsyncSearch> _factory;

        /// <summary>
        /// Search interface
        /// </summary>
        internal IAsyncSearch Search { get; private set; }

        /// <summary>
        /// ctor
        /// </summary>
        internal Client()
            : this("http://localhost:8086/Search")
        {
        }

        /// <summary>
        /// TfsGiggleUrl or TfsUrl
        /// </summary>
        /// <param name="url"></param>
        internal Client(string url)
        {
            if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException("url");
            Trace.TraceInformation("Client:url:{0}", url);

            try
            {
                Uri searchConnectionUri;
                var binding = GetBinding();

                if (url.ToUpperInvariant().Trim().Contains("/TFS/"))
                {
                    Trace.TraceInformation("Client:url appears to be a tfs url");
                    var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(url));
                    tfs.EnsureAuthenticated();
                    Trace.TraceInformation("Client:Connected to tfs at {0}", url);

                    var uri = GetRegisteredTfsGiggleUri(tfs);
                    if (uri == null)
                    {
                        throw new ArgumentException("No TfsGiggle service is registered");
                    }
                    var newUrl = string.Format("{0}://{1}:{2}/Search", binding.Scheme, uri.DnsSafeHost, uri.Port + 1);
                    searchConnectionUri = new Uri(newUrl);
                    Trace.TraceInformation("Client:SearchConnectionUri is:{0}", searchConnectionUri);
                }
                else
                {
                    searchConnectionUri = new Uri(url);
                }
                var address = new EndpointAddress(searchConnectionUri);
                _factory = new ChannelFactory<IAsyncSearch>(binding, address);
                Search = _factory.CreateChannel();
                Trace.TraceInformation("Client:Channel created");
            }
            catch (Exception ex)
            {
                Trace.TraceError("Exception caught:{0}", ex.ToString());
                throw;
            }
        }

        /// <summary>
        /// Get the current TfsGiggle Mex binding
        /// </summary>
        /// <param name="binding"></param>
        /// <returns></returns>
        internal static Binding GetMexBinding(Binding binding)
        {
            if (binding is NetTcpBinding)
            {
                return MetadataExchangeBindings.CreateMexTcpBinding();
            }
            return MetadataExchangeBindings.CreateMexHttpBinding();
        }

        /// <summary>
        /// Get the current TfsGiggle binding
        /// </summary>
        /// <returns></returns>
        internal static Binding GetBinding()
        {
            var tcpBinding = new NetTcpBinding();
            tcpBinding.TransferMode = TransferMode.Streamed;
            tcpBinding.Security.Mode = SecurityMode.None;
            tcpBinding.MaxReceivedMessageSize = Int32.MaxValue;
            tcpBinding.MaxBufferSize = Int32.MaxValue;
            return tcpBinding;
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_factory != null)
                {
                    _factory.Close();
                    _factory = null;
                }
                if (Search != null)
                {
                    ((IClientChannel)Search).Close();
                    Search = null;
                }
            }
        }

        /// <summary>
        /// Gets the registered TfsGiggle uri saved in the Tfs Registry
        /// </summary>
        /// <param name="tfs"></param>
        /// <returns></returns>
        internal static Uri GetRegisteredTfsGiggleUri(TfsTeamProjectCollection tfs)
        {
            var registry = tfs.GetService<ITeamFoundationRegistry>();
            const string TfsGiggleUri = "/Service/Registration/RegistrationExtendedAttribute/TfsGiggle/Uri";
            var tfsGiggleUri = registry.GetValue(TfsGiggleUri);
            if (string.IsNullOrWhiteSpace(tfsGiggleUri))
            {
                Trace.TraceInformation("Client.GetRegisteredTfsGiggleUri:No TfsGiggle service registered at {0}", TfsGiggleUri);
                return null;
            }
            var uri = new Uri(tfsGiggleUri);
            return uri;
        }

        /// <summary>
        /// Go away
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}